package com.ilearn.iodata.iolingo.utils;

import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpMethod;
import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.deserializer.DeserializerException;
import org.apache.olingo.server.core.ODataHandlerException;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

import static com.ilearn.iodata.iolingo.constant.OdataConst.NAMESPACE;

public class UrlInit
{

    private static final String REQUESTMAPPING = "requestMapping";

    /**
     * Creates the o data request.
     *
     * @param httpRequest the http request
     * @param split       the split
     * @return the o data request
     * @throws ODataHandlerException, SerializerException
     *                                the o data translated exception
     */
    public static ODataRequest createODataRequest(final HttpServletRequest httpRequest,
                                            final int split) throws ODataLibraryException {
        try {
            ODataRequest odRequest = new ODataRequest();

            odRequest.setBody(httpRequest.getInputStream());
            odRequest.setProtocol(httpRequest.getProtocol());
            odRequest.setMethod(extractMethod(httpRequest));
            copyHeaders(odRequest, httpRequest);
            fillUriInformation(odRequest, httpRequest, split);

            return odRequest;
        } catch (final IOException e) {
            throw new DeserializerException("An I/O exception occurred.", e,
                    DeserializerException.MessageKeys.IO_EXCEPTION);
        }
    }


    public static void fillUriInformation(final ODataRequest odRequest, final HttpServletRequest httpRequest, final int split) {

        String rawRequestUri = httpRequest.getRequestURL().toString();


        String rawODataPath;
        //Application need to set the request mapping attribute if the request is coming from a spring based application
        if (httpRequest.getAttribute(REQUESTMAPPING) != null) {
            String requestMapping = httpRequest.getAttribute(REQUESTMAPPING).toString();
            int beginIndex = rawRequestUri.indexOf(requestMapping) + requestMapping.length();
            rawODataPath = rawRequestUri.substring(beginIndex);
        } else if (!"".equals(httpRequest.getServletPath())) {
            String servletPath = httpRequest.getServletPath();
            rawODataPath = servletPath.substring(servletPath.indexOf(NAMESPACE) + NAMESPACE.length());
        } else if (!"".equals(httpRequest.getContextPath())) {
            String contextPath = httpRequest.getContextPath();
            rawODataPath = contextPath.substring(contextPath.indexOf(NAMESPACE) + NAMESPACE.length());
        } else {
            String requestURI = httpRequest.getRequestURI();
            rawODataPath = requestURI.substring(requestURI.indexOf(NAMESPACE) + NAMESPACE.length());
        }

        String rawServiceResolutionUri = null;
        if (split > 0) {
            rawServiceResolutionUri = rawODataPath;
            for (int i = 0; i < split; i++) {
                int index = rawODataPath.indexOf('/', 1);
                if (-1 == index) {
                    rawODataPath = "";
                    break;
                } else {
                    rawODataPath = rawODataPath.substring(index);
                }
            }
            int end = rawServiceResolutionUri.length() - rawODataPath.length();
            rawServiceResolutionUri = rawServiceResolutionUri.substring(0, end);
        }

        String rawBaseUri = rawRequestUri.substring(0, rawRequestUri.length() - rawODataPath.length());


        System.out.println("rawRequestUri:" + rawRequestUri);
        System.out.println("getServletPath:" + httpRequest.getServletPath());
        System.out.println("getContextPath:" + httpRequest.getContextPath());
        System.out.println("rawODataPath:" + rawODataPath);
        System.out.println("rawServiceResolutionUri:" + rawServiceResolutionUri);


        odRequest.setRawQueryPath(httpRequest.getQueryString());
        odRequest.setRawRequestUri(rawRequestUri
                + (httpRequest.getQueryString() == null ? "" : "?" + httpRequest.getQueryString()));
        odRequest.setRawODataPath(rawODataPath);
        odRequest.setRawBaseUri(rawBaseUri);
        odRequest.setRawServiceResolutionUri(rawServiceResolutionUri);
    }


    public static void copyHeaders(ODataRequest odRequest, final HttpServletRequest req) {
        for (final Enumeration<?> headerNames = req.getHeaderNames(); headerNames.hasMoreElements(); ) {
            final String headerName = (String) headerNames.nextElement();
            @SuppressWarnings("unchecked")
            // getHeaders() says it returns an Enumeration of String.
            final List<String> headerValues = Collections.list(req.getHeaders(headerName));
            odRequest.addHeader(headerName, headerValues);
        }
    }


    public static HttpMethod extractMethod(final HttpServletRequest httpRequest) throws ODataLibraryException {
        final HttpMethod httpRequestMethod;
        try {
            httpRequestMethod = HttpMethod.valueOf(httpRequest.getMethod());
        } catch (IllegalArgumentException e) {
            throw new ODataHandlerException("HTTP method not allowed" + httpRequest.getMethod(), e,
                    ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, httpRequest.getMethod());
        }
        try {
            if (httpRequestMethod == HttpMethod.POST) {
                String xHttpMethod = httpRequest.getHeader(HttpHeader.X_HTTP_METHOD);
                String xHttpMethodOverride = httpRequest.getHeader(HttpHeader.X_HTTP_METHOD_OVERRIDE);

                if (xHttpMethod == null && xHttpMethodOverride == null) {
                    return httpRequestMethod;
                } else if (xHttpMethod == null) {
                    return HttpMethod.valueOf(xHttpMethodOverride);
                } else if (xHttpMethodOverride == null) {
                    return HttpMethod.valueOf(xHttpMethod);
                } else {
                    if (!xHttpMethod.equalsIgnoreCase(xHttpMethodOverride)) {
                        throw new ODataHandlerException("Ambiguous X-HTTP-Methods",
                                ODataHandlerException.MessageKeys.AMBIGUOUS_XHTTP_METHOD, xHttpMethod, xHttpMethodOverride);
                    }
                    return HttpMethod.valueOf(xHttpMethod);
                }
            } else {
                return httpRequestMethod;
            }
        } catch (IllegalArgumentException e) {
            throw new ODataHandlerException("Invalid HTTP method" + httpRequest.getMethod(), e,
                    ODataHandlerException.MessageKeys.INVALID_HTTP_METHOD, httpRequest.getMethod());
        }
    }
}
